package com.example.king.ffmpegexample;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * 简单来说码流是这样的byte数组:0001+SPS+0001+PPS+0001+NALU+0001+NALU+…，
 * 解码开始时，需要先把0001+SPS+0001+PPS"喂"给解码器，
 * 然后将后面的一帧数据0001+NALU，继续"喂"给解码器, 然后得到一张解码出来的yuv, 用opengl es做渲染,
 * 再"喂"下一帧数据0001+NALU，得到一张解码出来的yuv, 用opengl es做渲染，..., 这样循环直到码流结束．
 *
 * */

public class H264AndroidGLActivity extends Activity {

    MyHandler mHandler;
    GL2JNIView gl2JNIView;

    private final static int MSG = 100;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        gl2JNIView = new GL2JNIView(this);

        setContentView(gl2JNIView);

        mHandler = new MyHandler(gl2JNIView);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    // Menu item Ids
    public static final int PLAY_ID = Menu.FIRST;
    public static final int EXIT_ID = Menu.FIRST + 1;

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        menu.add(0, PLAY_ID, 0, R.string.play);
        menu.add(0, EXIT_ID, 1, R.string.exit);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case PLAY_ID:
            {
                mHandler.sendEmptyMessageDelayed(MSG, 40);

                return true;
            }
            case EXIT_ID:
            {
                mHandler.removeMessages(MSG);
                finish();
                return true;
            }
        }
        return super.onOptionsItemSelected(item);
    }


    private static class MyHandler extends Handler {
        private WeakReference<GL2JNIView> ref;
        public MyHandler(GL2JNIView view) {
            ref = new WeakReference<>(view);
        }

        @Override
        public void handleMessage(Message msg) {
            GL2JNIView view = ref.get();
            if (view != null) {
                if (msg.what == MSG) {
                    view.refreshFrame();
                    sendEmptyMessageDelayed(MSG, 40);
                }
            }
        }
    }
}


class GL2JNIView extends GLSurfaceView {


    public GL2JNIView(Context context) {
        super(context);

        setEGLConfigChooser(8, 8, 8, 0, 16, 0);
        setEGLContextClientVersion(3);
        setRenderer(new MyRenderer());

        setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    public void refreshFrame() {
        requestRender();
    }

}


class MyRenderer implements GLSurfaceView.Renderer {

    private GLESRenderUtil renderUtil;

    int mTrans=0x0F0F0F0F;
    String PathFileName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/butterfly.h264";
    FileInputStream fileIS=null;

    int iTemp=0;
    int nalLen;

    boolean bFirst=true;
    boolean bFindPPS=true;

    int bytesRead=0;
    int NalBufUsed=0;
    int SockBufUsed=0;

    byte [] NalBuf = new byte[40980]; // 40k
    byte [] SockBuf = new byte[2048];


    public MyRenderer() {
        renderUtil = new GLESRenderUtil();

        try
        {
            fileIS = new FileInputStream(PathFileName);
        }
        catch(IOException e)
        {
            e.printStackTrace();
            return ;
        }
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        renderUtil.init();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        renderUtil.resize(width, height);
    }

    @Override
    public void onDrawFrame(GL10 gl) {
        try
        {
            bytesRead = fileIS.read(SockBuf, 0, 2048);
        }
        catch (IOException e) {}

        if(bytesRead<=0) {
            Log.d("kkunion", "-------------bytesRead:"+bytesRead);
            return;
        }

        SockBufUsed =0;

        while(bytesRead-SockBufUsed>0)
        {
            nalLen = MergeBuffer(NalBuf, NalBufUsed, SockBuf, SockBufUsed, bytesRead-SockBufUsed);

            NalBufUsed += nalLen;
            SockBufUsed += nalLen;

            while(mTrans == 1)
            {
                mTrans = 0xFFFFFFFF;

                if(bFirst==true) // the first start flag
                {
                    bFirst = false;
                }
                else  // a complete NAL data, include 0x00000001 trail.
                {
                    if(bFindPPS==true) // true
                    {
                        if( (NalBuf[4]&0x1F) == 7 )
                        {
                            bFindPPS = false;
                        }
                        else
                        {
                            NalBuf[0]=0;
                            NalBuf[1]=0;
                            NalBuf[2]=0;
                            NalBuf[3]=1;

                            NalBufUsed=4;

                            break;
                        }
                    }
                    //	decode nal
                    renderUtil.step(NalBuf, NalBufUsed - 4);
                }

                NalBuf[0]=0;
                NalBuf[1]=0;
                NalBuf[2]=0;
                NalBuf[3]=1;

                NalBufUsed=4;
            }
        }

    }


    int MergeBuffer(byte[] NalBuf, int NalBufUsed, byte[] SockBuf, int SockBufUsed, int SockRemain)
    {
        int  i=0;
        byte Temp;

        for(i=0; i<SockRemain; i++)
        {
            Temp  = SockBuf[i+SockBufUsed];
            NalBuf[i+NalBufUsed]=Temp;

            mTrans <<= 8;
            mTrans  |= Temp;

            if(mTrans == 1) // 找到一个开始字
            {
                i++;
                break;
            }
        }

        return i;
    }
}


